﻿using System.Collections.Generic;
using System.IO;
using System.Text;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.Editor;

namespace Peter.Logic.AvalonEdit
{
   /// <summary>
   /// Output formatter that creates a dictionary from AST nodes to segments in the output text.
   /// </summary>
   public class SegmentTrackingOutputFormatter : TextWriterOutputFormatter
   {
      readonly Dictionary<AstNode, ISegment> m_Segments = new Dictionary<AstNode, ISegment> ();
      readonly Stack<int> m_StartOffsets = new Stack<int> ();
      readonly StringWriter m_StringWriter;

      /// <summary>
      /// Gets the segments.
      /// </summary>
      public IDictionary<AstNode, ISegment> Segments
      {
         get { return m_Segments; }
      }

      public SegmentTrackingOutputFormatter (StringWriter stringWriter)
         : base (stringWriter)
      {
         this.m_StringWriter = stringWriter;
      }

      public static IDictionary<AstNode, ISegment> WriteNode (StringWriter writer, AstNode node, CSharpFormattingOptions policy, ICSharpCode.AvalonEdit.TextEditorOptions options)
      {
         var formatter = new SegmentTrackingOutputFormatter (writer);
         formatter.IndentationString = options.IndentationString;
         var visitor = new CSharpOutputVisitor (formatter, policy);
         node.AcceptVisitor (visitor);
         return formatter.Segments;
      }

      public override void StartNode (AstNode node)
      {
         base.StartNode (node);
         m_StartOffsets.Push (m_StringWriter.GetStringBuilder ().Length);
      }

      public override void EndNode (AstNode node)
      {
         int startOffset = m_StartOffsets.Pop ();
         StringBuilder b = m_StringWriter.GetStringBuilder ();
         int endOffset = b.Length;
         while (endOffset > 0 && b[endOffset - 1] == '\r' || b[endOffset - 1] == '\n')
            endOffset--;
         m_Segments.Add (node, new TextSegment { StartOffset = startOffset, EndOffset = endOffset });
         base.EndNode (node);
      }
   }
}
